export default class Snake {
    // 蛇头
    head: HTMLElement;
    // 蛇身体
    bodies: HTMLCollection;
    // 蛇容器
    snakeContainer: HTMLElement;
    // 父容器舞台的dom节点
    stage: HTMLElement
    // 蛇的移动步长
    step: number = 10
    // 蛇的移动方向
    direction: string = '';

    constructor() {
        // 获取蛇容器的dom节点
        this.snakeContainer = document.getElementById('snake')!
        // 获取蛇头的dom节点(会获取#snake标签下第一个div标签节点)
        this.head = <HTMLElement>document.querySelector('#snake > div')
        // 获取蛇身体的dom节点(会获取#snake标签下的所有的div标签节点)
        this.bodies = this.snakeContainer.getElementsByTagName('div')
        // 获取蛇容器的父容器舞台的dom节点
        this.stage = <HTMLElement>this.snakeContainer.parentElement
    }

    // 获取蛇头的X轴坐标
    get X() {
        return this.head.offsetLeft
    }
    // 获取蛇头的Y轴坐标
    get Y() {
        return this.head.offsetTop
    }
    // 设置蛇头的X轴坐标
    set X(newValue: number) {
        // 如果新值与旧值相同，则不用修改
        if (newValue === this.X) return
        // 如果蛇头触碰左侧或者右侧舞台边界
        if (newValue < 0 || newValue > this.stage.clientWidth - this.head.offsetWidth) {
            // 抛出错误并终止函数执行
            throw new Error('蛇撞墙了！')
        }
        // 如果蛇的身体长度大于一节并且蛇头的新横坐标与第二节身体的横坐标相同
        if (this.bodies.length > 1 && newValue === (<HTMLElement>this.bodies[1]).offsetLeft) {
            // 如果新方向与旧方向有冲突，则将新方向改为旧方向
            newValue = newValue > this.X ? this.X - this.step : this.X + this.step
        }
        // 调用移动蛇身体的方法
        this.moveSnakeBody()
        // 更新蛇头位置
        this.head.style.left = newValue + 'px'
        // 调用检查蛇头是否有触碰到蛇身体的方法
        this.checkSnakeHeadTouchBody()
    }
    // 设置蛇头的Y轴坐标
    set Y(newValue: number) {
        // 如果新值与旧值相同，则不用修改
        if (newValue === this.Y) return
        // 如果蛇头触碰上侧或者下侧舞台边界
        if (newValue < 0 || newValue > this.stage.clientHeight - this.head.offsetHeight) {
            // 抛出错误并终止函数执行
            throw new Error('蛇撞墙了！')
        }
        // 如果蛇的身体长度大于一节并且蛇头的新纵坐标与第二节身体的纵坐标相同
        if (this.bodies.length > 1 && newValue === (<HTMLElement>this.bodies[1]).offsetTop) {
            // 如果新方向与旧方向有冲突，则将新方向改为旧方向
            newValue = newValue > this.Y ? this.Y - this.step : this.Y + this.step
        }
        // 调用移动蛇身体的方法
        this.moveSnakeBody()
        // 更新蛇头位置
        this.head.style.top = newValue + 'px'
        // 调用检查蛇头是否有触碰到蛇身体的方法
        this.checkSnakeHeadTouchBody()
    }
    // 添加蛇身体长度的方法
    addBody() {
        // 在结束标签的前面加一个标签
        this.snakeContainer.insertAdjacentHTML('beforeend', '<div></div>')
    }
    // 移动蛇身体的方法
    moveSnakeBody() {
        // 遍历蛇的身体(从后往前开始遍历)
        for (let i = this.bodies.length - 1; i > 0; i--) {
            // 获取前一节身体
            const frontBody = <HTMLElement>this.bodies[i - 1]
            // 获取前一节身体的坐标
            const frontBodyX = frontBody.style.left
            const frontBodyY = frontBody.style.top
            // 获取当前节身体
            const currentBody = <HTMLElement>this.bodies[i]
            // 更新当前节身体的坐标
            currentBody.style.left = frontBodyX
            currentBody.style.top = frontBodyY
        }
    }

    // 检查蛇头是否有触碰到蛇身体的方法
    checkSnakeHeadTouchBody() {
        // 遍历所有的蛇身体
        for (let i = 1; i < this.bodies.length; i++) {
            // 获取当前节身体
            const currentBody = <HTMLElement>this.bodies[i]
            // 获取当前节身体的坐标
            const currentBodyX = currentBody.offsetLeft
            const currentBodyY = currentBody.offsetTop
            // 如果当前节身体的坐标与蛇头坐标重叠
            if (currentBodyX === this.X && currentBodyY === this.Y) {
                // 抛出错误
                throw new Error('蛇撞到自己')
            }
        }
    }
}